Repository navigation
docs(pm-dispatch): the draft-conversion reading splits by queue state — no queued PR left the queue on a draft conversion - #20845
Conversation
… — a queued PR kept its place in every re-read platform-readings `:41-:43` re-read against the timelines they rest on. Unqueued and armed, a draft conversion drops auto-merge in the same second (re-arm after ready). Queued, no draft conversion was seen to dequeue in this repo: two PRs merged through the queue while in draft, and two others left only after their own merge-group builds went red. The remedy line keeps both acts and records that both together were not seen to dequeue either. Net 0 lines; each rewritten line is at most 120 bytes. Claude-Session: https://claude.ai/code/session_01KTZmMfzVzjNvyaLyQ8mHvg Co-authored-by: Claude <noreply@anthropic.com>
Contract reviewServed-tier: Inputs: card #20764 (body and all three comments: triage direction 5903760674, claim 5910003827, The diff. One file, Check-runs on the head ( ① Derived judgmentsThe timelines, as this reviewer read them (
Every row of the PR body's two tables matches what this reviewer read. In the four queued cases the draft conversion was followed by no removal event of its own; in the three unqueued cases the conversion was followed by
The departure from the triage direction ( Citations by date, not PR number. Confirmed: Pins. 469 / 469 lines hold (net 0); new lines 120 / 118 / 120 bytes, none over the 120-byte cap; table-row pin 0 unchanged (the file has no table). The dev's ② Semver level
③ Boundary flagsDev deviations (5910417690), each answered:
Out-of-scope findings:
Landing order. PR #20854's new Prime Directive 14 text cites this file's queue-membership section (" Landing key. The diff is Tier S ( Implemented-by: VERDICT: PASS |
…R's auto-merge; a queued governed pull-back goes to the maintainer (objectstack-ai#20854) Fixes objectstack-ai#20847 Clause-②: no ## What changes Four sentences stop saying that a draft conversion, or draft plus an auto-merge disable, removes a PR from the merge queue. Each keeps the half that holds: on an **unqueued** PR, the conversion drops auto-merge. Prime Directive 14 now names the one removal act that is measured, the maintainer's manual removal, and says why a seat cannot do it. | site | before | after | |:--|:--|:--| | `AGENTS.md` Prime Directive 14, "Already armed or queued" (was `:285-:287`, now `:285-:288`) | Convert it back to **draft** AND disable auto-merge — draft is what removes queue membership, disabling alone drops only the arming — then confirm from the remote that it is in neither the queue nor `origin/main`. | Disable auto-merge AND convert it back to **draft**, which disarms an unqueued PR; no seat act, those two included, is measured to dequeue one (`platform-readings.md`, queue membership), so ask the maintainer at once to remove a queued PR by hand — then confirm from the remote that it is in neither the queue nor `origin/main`. | | `AGENTS.md` "State on your PR" (`:428-:429` → `:429-:430`) | the draft flag, which flipped back destroys auto-merge and queue membership at once | the draft flag, which flipped back destroys an unqueued PR's auto-merge at once | | `AGENTS.md` multi-agent §7 (`:532-:533` → `:533-:534`) | **flipping back to draft drops auto-merge and queue membership at once**, and neither returns by itself | **flipping back to draft drops an unqueued PR's auto-merge at once**, and it does not return by itself | | `.claude/skills/pm-dispatch/SKILL.md:202` | 已入队才读到本条 ⇒ 转 draft 与 disable 都做;出队以阳性探针答,ref 缺席只旁证。 | 已入队 ⇒ 转 draft 并 disable,即请维护者手动出队(席位无实测手段);出队以阳性探针答。 | Every clause that still holds is kept: "**Draft is no barrier by itself — the barrier is this directive**", "confirm from the remote that it is in neither the queue nor `origin/main`", "ready *first*, arm *second*", and the positive-probe reading of a dequeue. The rule text cites `platform-readings.md` and restates no reading. The readings themselves are in PR objectstack-ai#20845 (issue objectstack-ai#20764). **The grep.** `AGENTS.md` and every file under `.claude/**` were searched for other copies of the claim: `queue membership`, draft next to queue or auto-merge, 转 draft, 出队, 踢队, dequeue, evict. The four sites above are all of them. `references/platform-readings.md:41-:43` is PR objectstack-ai#20845's, and this PR leaves it alone. `lanes/cli.md:17` only points at that file. ## Is there a seat act that removes a queued PR? (the claim's measurement) Every step was read-only. ⛔ No mutation was called and no PR was dequeued. 1. **Live GraphQL introspection was refused.** The request was `POST https://api.github.com/graphql` with `query { __type(name: "Mutation") { fields { name description } } }`. It returned **403** from this session's egress, with the message "GitHub GraphQL is not available from Claude Code sessions". The same message lists the pull-request routes the egress does serve: `ccr/auto_merge` (PUT/DELETE), `ccr/ready_for_review` and `ccr/convert_to_draft`. None of them dequeues. 2. **GitHub's published GraphQL schema does declare the mutation.** This is a document reading, not a live introspection. The source is `octokit/graphql-schema` `schema.graphql` on `main`, fetched 2026-09-30T12:18Z (1,223,842 bytes, sha256 prefix `3c62d0526d133cee`). It declares `dequeuePullRequest(input: DequeuePullRequestInput!)` with the description "Remove a pull request from the merge queue." Its input is `id: ID!`, the pull request id. 3. **REST has no dequeue endpoint.** In `github/rest-api-description`, `api.github.com.json` (info version 1.1.4, `main`), no path contains `queue`. The word "dequeue" appears only in the `pull_request.dequeued` webhook schemas. 4. **GitHub's own docs list the ways a PR leaves the queue.** The source is `github/docs` on `main` (`data/reusables/pull_requests/merge-queue-removal-reasons.md`). The list has four entries: CI failure, timeout, "User requesting a removal via the API or merge queue interface", and a branch-protection failure. Neither a draft conversion nor a close is on it. The how-to page (`merging-a-pull-request-with-a-merge-queue.md`) removes a PR through the web page's **Remove from queue** button and says the CLI cannot do it. 5. **The relay cannot carry it.** `scripts/pm/fleet-write/ops.mjs` at `05a7547c9` has 15 ops: comment, comment_edit, labels_add, labels_remove, assign, unassign, issue_patch, issue_create, pr_create, pr_request_reviewers, pr_ready, pr_draft, automerge_enable, automerge_disable and transfer. `ALLOWED_MUTATIONS` has five entries, and `dequeuePullRequest` is not one of them. ⇒ **No dequeue act is measured to be available to a seat.** The platform documents one (the GraphQL mutation), but neither this session's egress nor the relay can reach it. So the rule sends a queued pull-back to the maintainer and says why. The triage direction on the card reserves the next step for the triage seat: whether to file a tooling child for a relay dequeue op. That would be a new relay capability. ## Coordination with PR objectstack-ai#20845 The new rule cites the queue-membership section of `platform-readings.md`. At `origin/main` (`05a7547c9`), line `:42` of that section still carries the old reading, and PR objectstack-ai#20845 rewrites `:41-:43`. Until objectstack-ai#20845 lands, the rule and line `:42` disagree. objectstack-ai#20845 should land first, or with this PR. The citation names a file section, not a PR, so it is correct once both have landed, whatever the order. ## Line budget - `AGENTS.md`: 1107 → **1108** lines, against a ceiling of 1116 (headroom 9 → 8). The +1 is all in Prime Directive 14. The new sentence adds the maintainer route and the reason for it, and it needs four lines where the old one took three. The other two `AGENTS.md` sentences are net 0. - `pm-dispatch/SKILL.md`: 319 → **319** lines (ceiling 319, headroom 0). Line `:202` is edited in place and goes from 107 to 116 bytes. - Every added line is at most 120 bytes: 118 / 110 / 120 / 118 (Prime Directive 14), 83 / 87 ("State on your PR"), 88 / 88 (§7) and 116 (`SKILL.md`). `check:pm-skill-ratchet` reports no over-length line and leaves the table-row pins unchanged (768 and 342). ## Deviations from the dispatch 1. **`SKILL.md:202` drops two parts.** They are the clause 「ref 缺席只旁证」 and the 「才读到本条」 of the trigger. The 120-byte cap left 13 bytes of room on the line, and the maintainer route with its reason needs about 50. The dropped clause is kept in `platform-readings.md:44` (「ref 缺席只作旁证,⛔ 永不承载结论」), and the positive-probe half of the line stays. 2. **`AGENTS.md` is net +1, not ≤ 0.** None of the three sentences frees a whole line by deleting content. Freeing one would need a re-wrap of text this PR does not otherwise change. The pin is not exceeded. 3. **The introspection is a schema-document reading.** The live endpoint refused this session (item 1 above). ## Acceptance notes - **Same family, outside this PR's scope (the dispatch rules out any script edit).** `scripts/pm/check-governed-queue-guard.mjs` makes the same claim in both of its refusal texts, at `:1451-:1452` and `:1911-:1912` (at `05a7547c9`): "take the pull request out of the queue: convert it back to DRAFT (disarming auto-merge alone does NOT dequeue it)". Parking the PR in draft after the guard's red is still useful: an armed PR re-enters the queue, and a draft cannot be armed. The part in parentheses is the falsified premise. This is reported to the seat so it can join the card's family. No card is filed. - **An unmeasured candidate, for triage.** Closing the PR is an act a seat has, through the relay's `issue_patch` with `state: closed`. A code comment in `.github/workflows/cross-repo-issue-closer.yml:399-:401` says that closing drops queue membership. GitHub's list of removal reasons does not name it, and I know of no reading of it in this repo. It was not attempted. - **Changeset.** None. `.claude/**` and `AGENTS.md` are not in any package's `files[]`, so this PR publishes nothing. `skip-changeset` is for the seat to apply. - **Landing.** This is a Tier H governed surface (`AGENTS.md`). The PR stays a draft until an authorized approval. ## Verification (head `b095ba02e`) - `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands`, with no paths, derived 22 commands for this change set (2 paths against merge base `05a7547c9`). All 22 exited 0: - `check-closing-keyword-parity`, plus its `--self-test`; - `check-comment-mask-corpus`; - `check-governed-queue-guard --self-test` and `check-harness-current --self-test`; - `@objectstack/lint check:doc-formula-expressions`, run after building `@objectstack/lint` and its dependency closure under `os-verify-lock.sh` (`VERDICT command-exit 0`); - the `check:` family: `agent-test-spelling`, `cross-package-test-inputs`, `doc-authoring`, `docs-audit-scope`, `driver-memory-census`, `gitlink-declared`, `nul-bytes`, `pm-expected-skips`, `pm-governed-merges`, `pm-governed-prose`, `pm-skill-id-lint`, `pm-skill-ratchet`, `refd-timer-probe`, `required-contexts`, `skill-frame-sync` and `watch-hint-literal`. - `pnpm check:pm-settings-deny-roster` was also run, because its roster lives under `.claude`. It exited 0. - The `--ran` reconciliation read "22 derived, 22 run, 0 NOT-MEASURED, 0 UNRUN". All 22 commands recorded an exit code. - Readings from the gates: - `check:pm-skill-ratchet`: "AGENTS.md is 1108 lines (ceiling 1116; headroom 8)" and "pm-dispatch/SKILL.md is 319 lines (ceiling 319; headroom 0)". - `check:pm-skill-id-lint`: "34 file(s) clean". - `check:pm-governed-prose`: "2 instruction surface(s) name all 6 registered governed surfaces". - NOT MEASURED: - `check-required-contexts --verify-required-set`, which reads the workflow event and so runs only in CI; - the four CI type-check lanes. This diff touches no TypeScript. ## 维护者速读(草稿) - **改了什么**:`AGENTS.md` 三句、`pm-dispatch/SKILL.md` 一行。旧文说「转 draft 会把 PR 踢出合并队列」,现在都不再这么说。改后:转 draft 只在**未入队**时掉 auto-merge。已入队的受管 PR 要撤回时,两手照做,并**立即请你手动出队**。 - **为什么改**:实测里 4 个已入队后转 draft 的 PR 都没有因此出队,其中 2 个带着 draft 被队列直接合入,读数在 PR objectstack-ai#20845。「转 draft 能出队」的说法写在第 14 条指令里,那是防止坏合并的撤回手段,而这个手段实际不起作用。**出队手段问题**:目前席位**没有**经实测可用的出队手段。GitHub 公开 schema 里有 GraphQL `dequeuePullRequest`,但本会话出口拒绝 GraphQL(403),中继也没有这个 op。所以规则暂时把已入队的撤回交给你手动出队。要不要给中继加出队 op,属于新能力,由分诊决定是否立子卡。子卡立了,你会看到裁决请求。 - **风险与代价(含回滚)**:只改规则文字,不改代码。代价是已入队的撤回要等你点「Remove from queue」。队列落地约需 15–30 分钟,所以需要你及时响应。PR objectstack-ai#20845 应先合或同合,否则被引用的读数第 42 行暂时和本规则说法相反。回滚就是 revert 这一个 PR。 - **席位意见**: - **你要做的**:审阅后批准本 PR(或直接合并)。 --- _Generated by [Claude Code](https://claude.ai/code/session_01KTZmMfzVzjNvyaLyQ8mHvg)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
Fixes #20764
Clause-②: no
What changes
.claude/skills/pm-dispatch/references/platform-readings.md, lines 41-43 only. The rewrite is net 0 lines, and the three new lines are 120 / 118 / 120 bytes (cap 120).:41:42:43The remedy on
:43is unchanged: do both acts. No rule is added. The in-file citations are dates, becausepnpm check:pm-skill-id-lintgoes red on a#-number anywhere under.claude/skills/pm-dispatch/, and this file cites no PR number today. The PR numbers and timeline events are in this body. That is where the id-lint header puts a reading's provenance.The readings (re-readable:
GET /repos/objectstack-ai/objectstack/issues/N/timeline)I ran a read-only timeline scan over #4700-#4900, #6650-#6850 and #20400-#20840. It looked for two patterns: a
convert_to_draftbetweenadded_to_merge_queueand the nextremoved_from_merge_queue, and aconvert_to_drafton a PR that was armed but not queued. The scan found every row below. The table is complete for those windows only, not for the whole repo.Queued, then converted to draft. None of the four left the queue because of the conversion.
auto_merge_disabledeventpr-20420-3cf6449hadLint & Type Check= failure at 14:01:49Z, before the draft;github-merge-queue[bot]removed it at 14:11:24Z, unmergedpr-20442-b285508for it at 14:11:29Z, 9 min after the draft; that group'sCI= failure at 14:30:14Z; removed at 14:35:44Z, unmergedArmed, not queued, then converted to draft. Each one dropped auto-merge.
auto_merge_disabledEach disable event carries the converter as its actor. A timeline cannot tell whether the platform wrote it or a scripted follow-up did. The line records what was observed ("同秒").
Where the old reading came from
db6581a5(docs(skills): 补齐「跟到合并为止 / CI 诊断纪律 / 生成物同步」四类 PM 经验 (#4892) #4893, card 把「跟到合并为止 / CI 诊断纪律 / 生成物同步」写进 pm-dispatch 与 spec-property-retirement skill #4892, 2026-08-03). It cites no PR. That is the same day feat(spec)!: 移除./automation的孤儿EventSchema—— C6 双源清账,基线 13 → 12 (#4658) #4745 dropped auto-merge on an armed PR that was not queued. The queue-membership half has no instance behind it.cd704cc4(docs(pm-dispatch): ADR merge prohibition in Guardrails + ACCEPT path fork; probe-and-revive as the standing backstop; replay-wake identity check (#6741, #6747) #6799) later cited docs(adr),chore(devx): reconstruct ADR-0079 + make every cited ADR number resolve #6732 for "only draft evicts; disable alone does not". In docs(adr),chore(devx): reconstruct ADR-0079 + make every cited ADR number resolve #6732's own timeline the PR stays queued after the draft and is merged by the queue 34 minutes later.How this departs from the triage direction (5903760674)
I did not choose these quietly. Each one is a place where a measurement changed what the direction assumed:
:42is split by queue state, not "mixed" in the sense of opposite readings. No queued PR in the scanned windows left the queue because of a draft conversion. The direction assumed the old half had its own measurement. It has none, and the one PR later cited for it reads the other way. What does go both ways in this repo is the effect by state: unqueued, auto-merge drops; queued, the PR keeps its place.:43says "not seen to dequeue", not "not measured". Three PRs (docs(adr),chore(devx): reconstruct ADR-0079 + make every cited ADR number resolve #6732, feat(spec): ComponentPropsMap rows for action:button/group/menu/icon and element:definition-list/repeater #20420, feat(spec): declare the staged $empty filter operator and its per-type expansion (#20311) #20442) have both acts claimed in writing, and none was removed by them. The caveat: on a queued PR, the disable leaves no timeline event, so whether it was sent rests on the seats' own comments.:41loses "两向相反读数并存". No opposite reading survived the re-read.Acceptance notes
AGENTS.mdstates the falsified reading as a premise in three places. Prime Directive feat: Comprehensive CRM example demonstrating all ObjectStack protocol features #14 says "draft is what removes queue membership, disabling alone drops only the arming". Multi-agent §7 says "flipping back to draft drops auto-merge and queue membership at once". The "State on your PR" paragraph says the draft flag "flipped back destroys auto-merge and queue membership at once". I did not touch them. They are reported to the seat.removed_from_merge_queue2026-09-30T07:33:36Z). Whether the remedy should name a different act is a question for the seat. It is not a rule this PR adds..claude/**is not in any package'sfiles[], so this PR publishes nothing.skip-changesetis the seat's to apply..claude/**). This PR stays a draft until a## Contract reviewrecord atCONTRACT_REVIEW_TIERexists for its head.Verification (head
02ea176a1)node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commandsall exit 0. They arecheck-closing-keyword-parity(plus--self-test),check-comment-mask-corpus,check-governed-queue-guard --self-test,check-harness-current --self-test,lint check:doc-formula-expressions(after building@objectstack/lintand its dependency closure underos-verify-lock.sh),check:agent-test-spelling,check:cross-package-test-inputs,check:doc-authoring,check:driver-memory-census,check:gitlink-declared,check:nul-bytes,check:pm-governed-merges,check:pm-half-states,check:pm-skill-id-lint,check:pm-skill-ratchet,check:refd-timer-probe,check:required-contexts,check:skill-frame-syncandcheck:watch-hint-literal.pnpm check:pm-settings-deny-rosterwas also run, because its roster lives under.claude. Exit 0.--ranreconciliation: 20 derived, 20 run, 0 NOT-MEASURED. All 20 recorded an exit code.check:pm-skill-ratchetprinted:platform-readings.md is 469 lines (ceiling 469; headroom 0), widest table row 0 bytes (pin 0).check-required-contexts --verify-required-setandcheck-half-states --provenance, which read the workflow event, and the four CI type-check lanes. This diff touches no TypeScript.维护者速读(草稿)
platform-readings.md第 41-43 行,关于「已入队 PR 转 draft 能否踢出合并队列」的三条平台读数。行数不变。AGENTS.md里的同一旧说法一并修正(Tier H),需要你点头另开 PR。Generated by Claude Code